package cn.zhaoyun.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;

import java.time.Duration;

@Configuration
public class AppConfig {
    @Bean
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<Object, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);
        // 给 redisTemplate 指定默认序列化器, 向 redis 中存储时, k 和 v 都会以该序列化器进行序列化
        template.setDefaultSerializer(new Jackson2JsonRedisSerializer<Object>(Object.class));
        return template;
    }

    // SpringBoot2.x 自定义 RedisCacheManager
    @Bean
    @Primary // 如果有多个缓存管理器, 可以通过该注解将某个缓存管理器指定为默认的缓存管理器
    public RedisCacheManager redisCacheManager(RedisConnectionFactory factory) {
        //初始化一个RedisCacheWriter
        RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(factory);

        //设置CacheManager的值序列化方式为json序列化
        RedisSerializer<Object> jsonSerializer = new GenericJackson2JsonRedisSerializer();
        RedisSerializationContext.SerializationPair<Object> pair
                = RedisSerializationContext.SerializationPair.fromSerializer(jsonSerializer);
        RedisCacheConfiguration defaultCacheConfig
                = RedisCacheConfiguration.defaultCacheConfig().serializeValuesWith(pair);

        //设置默认超过期时间是30秒
        defaultCacheConfig.entryTtl(Duration.ofSeconds(30));

        //初始化RedisCacheManager
        return new RedisCacheManager(redisCacheWriter, defaultCacheConfig);
    }
    // 1.JdkSerializationRedisSerializer是默认序列化方式，是最简单的也是最安全的，只要实现了Serializer接口，实体类型，集合，Map等都能序列化与反序列化，
    // 但缺陷是序列化数据很多，会对redis造成更大压力，且可读性和跨平台基本无法实现
    // 2.Jackson2JsonRedisSerializer用的是json的序列化方式，能解决JdkSerializationRedisSerializer带来的缺陷，
    // 但复杂类型(集合,泛型,实体包装类)反序列化时会报错，且Jackson2JsonRedisSerializer需要指明序列化的类Class，
    // 这代表一个实体类就有一个RedisCacheManager，代码冗余
    // 3.最后查看源码，发现RedisSerializer（Jackson2JsonRedisSerializer也实现此接口）的实现类中有一个GenericJackson2JsonRedisSerializer，
    // 此类不用需要指明序列化的类，写一个RedisCacheManager即可，代码更精简，复杂类型(集合,泛型)反序列化时不会报错，
    // 查看redis数据可以发现实现原理是在json数据中放一个@class属性，指定了类的全路径包名，方便反序列化，所以在内存占用上高一点点，
    // 但是反序列化性能自测要比Jackson2JsonRedisSerializer高（有兴趣的老哥也可以自己试试性能差别），空间换时间，
    // 当然也因为此特性，项目一部分的结构（实体全类名）会json数据中体现，这部分就仁者见仁智者见智了。
    // 注：经过反复考虑，目前使用了GenericJackson2JsonRedisSerializer作为序列化方式
}
